/*
 * Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy)
 * Copyright (c) 2005 - 2006 CACE Technologies, Davis (California)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Politecnico di Torino, CACE Technologies
 * nor the names of its contributors may be used to endorse or promote
 * products derived from this software without specific prior written
 * permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifdef _MSC_VER
 /*
  * Disable warnings about deprecated and unsafe CRT functions,
  * as these examples can also be compiled on *nix systems.
  */
#define _CRT_SECURE_NO_WARNINGS
#endif

#include "pcap.h"
#include <time.h>


  /* 4 bytes IP address structure */
typedef struct ip_address
{
	u_char byte1;
	u_char byte2;
	u_char byte3;
	u_char byte4;
}ip_address;

/* IPv4 header structure */
typedef struct ip_header
{
	u_char	ver_ihl;		// Version (4 bits) + Internet header length (4 bits)
	u_char	tos;			// Type of service 
	u_short tlen;			// Total length 
	u_short identification; // Identification
	u_short flags_fo;		// Flags (3 bits) + Fragment offset (13 bits)
	u_char	ttl;			// Time to live
	u_char	proto;			// Protocol
	u_short crc;			// Header checksum
	ip_address	saddr;		// Source address
	ip_address	daddr;		// Destination address
	u_int	op_pad;			// Option + Padding
}ip_header;

/* UDP header structure */
typedef struct udp_header
{
	u_short sport;			// Source port
	u_short dport;			// Destination port
	u_short len;			// Datagram length
	u_short crc;			// Checksum
}udp_header;

/* Prototype of the packet handler function */
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data);

time_t start_time;
time_t cur_time;
time_t last_out;
FILE* csv;

int main()
{
	/* Initialize CSV file (output file name: statics.csv) */
	csv = fopen("statics.csv", "w");
	fprintf(csv, "time,mac_src,ip_src,mac_dst,ip_dst,length\n"); // Fix typo: "lenth" -> "length"
	fclose(csv);

	start_time = time(NULL);
	last_out = start_time;

	pcap_if_t* alldevs;
	pcap_if_t* d;
	int inum;
	int i = 0;
	pcap_t* adhandle;
	char errbuf[PCAP_ERRBUF_SIZE];
	u_int netmask;
	char packet_filter[] = "ip and udp"; // Filter rule: capture only IPv4 and UDP packets
	struct bpf_program fcode;

	/* Retrieve the list of network devices */
	if (pcap_findalldevs(&alldevs, errbuf) == -1)
	{
		fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
		exit(1);
	}

	/* Print the device list */
	for (d = alldevs; d; d = d->next)
	{
		printf("%d. %s", ++i, d->name);
		if (d->description)
			printf(" (%s)\n", d->description);
		else
			printf(" (No description available)\n");
	}

	/* Check if no devices are found */
	if (i == 0)
	{
		printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
		return -1;
	}

	/* Prompt user to enter the interface number */
	printf("Enter the interface number (1-%d):", i);
	scanf("%d", &inum);

	/* Check if the selected interface number is valid */
	if (inum < 1 || inum > i)
	{
		printf("\nAdapter number out of range.\n");

		/* Free the device list to avoid memory leak */
		pcap_freealldevs(alldevs);
		return -1;
	}

	/* Locate the selected interface in the device list */
	for (d = alldevs, i = 0; i < inum - 1; d = d->next, i++);

	/* Open the selected network interface */
	if ((adhandle = pcap_open_live(d->name,	// Name of the target device
		65536,			// Maximum portion of packet to capture (65536 ensures full packet capture for all MAC types)
		1,				// Promiscuous mode (non-zero = enabled)
		1000,			// Read timeout (in milliseconds)
		errbuf			// Buffer for error messages
	)) == NULL)
	{
		fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", errbuf); // Add errbuf to error message
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return -1;
	}

	/* Check link layer type (only Ethernet is supported for this program) */
	if (pcap_datalink(adhandle) != DLT_EN10MB)
	{
		fprintf(stderr, "\nThis program works only on Ethernet networks.\n");
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return -1;
	}

	/* Retrieve the subnet mask of the interface (for filter compilation) */
	if (d->addresses != NULL)
		/* Get mask from the first address of the interface */
		netmask = ((struct sockaddr_in*)(d->addresses->netmask))->sin_addr.S_un.S_addr;
	else
		/* Assume C-class network if interface has no addresses */
		netmask = 0xffffff;


	// Compile the BPF packet filter
	if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0)
	{
		fprintf(stderr, "\nUnable to compile the packet filter. Check the syntax.\n");
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return -1;
	}

	// Apply the compiled filter to the capture handle
	if (pcap_setfilter(adhandle, &fcode) < 0)
	{
		fprintf(stderr, "\nError setting the filter.\n");
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return -1;
	}

	/* Prompt user that capture is starting */
	printf("\nListening on %s...\n", d->description);

	/* Free the device list (no longer needed after interface is selected) */
	pcap_freealldevs(alldevs);

	/* Start packet capture (infinite loop until interrupted) */
	pcap_loop(adhandle, 0, packet_handler, NULL);

	return 0;
}



/* Function to format timestamp (not used in current logic) */
void format_timestamp(const struct timeval* timestamp) {
	struct tm* tm_info;
	char buffer[20]; // Buffer to store formatted time string

	time_t seconds = timestamp->tv_sec; // Get seconds part of timestamp
	tm_info = localtime(&seconds);      // Convert seconds to local time structure

	// Format time string as "YYYY-MM-DD HH:MM:SS"
	strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_info);

	// Print formatted time and microseconds
	printf("Formatted time: %s.%06ld\n", buffer, timestamp->tv_usec);
}

/* Callback function invoked by libpcap for each captured packet */
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data)
{
	struct tm* ltime;
	char timestr[20]; // Buffer for formatted timestamp
	ip_header* ih;    // Pointer to IPv4 header
	udp_header* uh;   // Pointer to UDP header
	u_int ip_len;     // Length of IPv4 header (in bytes)
	u_short sport;    // Source port (host byte order)
	u_short dport;    // Destination port (host byte order)
	time_t local_tv_sec; // Seconds part of packet timestamp
	// Source MAC address (starts at the beginning of Ethernet header)
	u_char* src_mac = (u_char*)pkt_data;
	// Destination MAC address (6 bytes offset from source MAC)
	u_char* dst_mac = src_mac + 6;


	/* Mark unused parameter to avoid compilation warning */
	(VOID)(param);

	/* Convert packet timestamp to human-readable format */
	local_tv_sec = header->ts.tv_sec;
	ltime = localtime(&local_tv_sec);
	strftime(timestr, sizeof timestr, "%Y-%m-%d %H:%M:%S", ltime);


	/* Locate the start of IPv4 header (skip 14-byte Ethernet header) */
	ih = (ip_header*)(pkt_data +
		14); // 14 bytes = length of Ethernet header

	/* Calculate IPv4 header length (low 4 bits of ver_ihl × 4 bytes) */
	ip_len = (ih->ver_ihl & 0xf) * 4;
	/* Locate the start of UDP header (skip IPv4 header) */
	uh = (udp_header*)((u_char*)ih + ip_len);

	/* Convert port numbers from network byte order to host byte order */
	sport = ntohs(uh->sport);
	dport = ntohs(uh->dport);

	/* Open CSV file in append mode (write new packet data) */
	csv = fopen("statics.csv", "a");
	/* Print timestamp to console */
	printf("%s", timestr);

	/* Print source MAC address to console */
	printf(",%02X-%02X-%02X-%02X-%02X-%02X",
		src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5]);

	/* Print source IP address to console */
	printf(",%d.%d.%d.%d",
		ih->saddr.byte1,
		ih->saddr.byte2,
		ih->saddr.byte3,
		ih->saddr.byte4);

	/* Print destination MAC address to console */
	printf(",%02X-%02X-%02X-%02X-%02X-%02X",
		dst_mac[0], dst_mac[1], dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5]);

	/* Print destination IP address to console */
	printf(",%d.%d.%d.%d",
		ih->daddr.byte1,
		ih->daddr.byte2,
		ih->daddr.byte3,
		ih->daddr.byte4);

	/* Print total packet length to console */
	printf(",%d ", header->len);

	printf("\n");

	/* Write timestamp to CSV file */
	fprintf(csv, "%s", timestr);

	/* Write source MAC address to CSV file */
	fprintf(csv, ",%02X-%02X-%02X-%02X-%02X-%02X",
		src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5]);

	/* Write source IP address to CSV file */
	fprintf(csv, ",%d.%d.%d.%d",
		ih->saddr.byte1,
		ih->saddr.byte2,
		ih->saddr.byte3,
		ih->saddr.byte4);

	/* Write destination MAC address to CSV file */
	fprintf(csv, ",%02X-%02X-%02X-%02X-%02X-%02X",
		dst_mac[0], dst_mac[1], dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5]);

	/* Write destination IP address to CSV file */
	fprintf(csv, ",%d.%d.%d.%d",
		ih->daddr.byte1,
		ih->daddr.byte2,
		ih->daddr.byte3,
		ih->daddr.byte4);

	/* Write total packet length to CSV file */
	fprintf(csv, ",%d ", header->len);

	fprintf(csv, "\n");

	/* Close CSV file to release resource */
	fclose(csv);

	//cur_time = time(NULL);
	///* Statistics output every 10 seconds (commented out, unimplemented) */
	//if (cur_time - start_time % 10 == 0 && last_out!=cur_time)
	//{

	//	last_out = cur_time;
	//}

}